/*  GMooc - A Java web application for delivering massive open online courses
*   Copyright (C) 2012 Charles A. Wight
*   
*    This program is free software: you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation, either version 3 of the License, or
*   (at your option) any later version.
*
*   This program is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU General Public License for more details.
*
*   You should have received a copy of the GNU General Public License
*   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.gmooc;

import java.io.Serializable;
import java.util.Date;

import javax.mail.internet.InternetAddress;
import javax.persistence.Id;
import javax.persistence.Transient;
import javax.servlet.http.HttpServletRequest;

import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.annotation.Cached;

@Cached
public class User implements Comparable<User>,Serializable {
	private static final long serialVersionUID = 137L;
	@Id private String id;
		private String email;
		private String lastName;
		private String firstName;
		private boolean anonymous=false;
		private int roles=0;
		private String authDomain;
		private Date lastLogin;
		private Date termsAgreedTo;
	
	@Transient static Objectify ofy = new DAO().ofy();

	User() {}

	User(String id,String authDomain,String em,String fn,String ln) {
		this.id = id;
		setAuthDomain(authDomain);
		setEmail(em);
		setFirstName(fn);
		setLastName(ln);
		setLastLogin();
	}

	static User createUserServiceUser(com.google.appengine.api.users.User u) {
		// this method provisions a new account for a UserService user
		if (u == null) return null;
		try {
			User user = new User(u.getUserId(),u.getAuthDomain(),u.getEmail(),"","");
			ofy.put(user);
			return user;
		} catch (Exception e) {
			return null;
		}
	}
	
	static User createBLTIUser(HttpServletRequest request) {
		// this method provisions a new account for a BLTI user
		try {
			String user_id = request.getParameter("user_id");
			String userId = request.getParameter("oauth_consumer_key") + (user_id==null?"":":"+user_id);
			String email = request.getParameter("lis_person_contact_email_primary");
			if (email==null) email = "";
			String firstName = request.getParameter("lis_person_name_given");
			if (firstName==null) firstName = "";
			String lastName = request.getParameter("lis_person_name_family");
			if (lastName==null) lastName = "";
			User user = new User(userId,"LTI",email,firstName,lastName);
			ofy.put(user);
			return user;
		} catch (Exception e) {
			return null;
		}
	}
	
	static User getInstance(String userId) {
		try {
			return ofy.get(User.class,userId);
		} catch (Exception e) {
			return null;
		}
	}
	
	String getId() {
		return this.id;
	}
	
	String getEmail() {
		return this.email==null?"":this.email;
	}
	
	void setEmail(String em) {
		try {
			new InternetAddress(em).validate();
			if (!em.contains("@")) throw new Exception();
			this.email = em.toLowerCase();
		} catch (Exception e) {
			this.email = "";
		}
	}
	
	String getFirstName() {
		return this.firstName.replaceAll("'","&#39;");
	}
	
	String getLastName() {
		return this.lastName.replaceAll("'","&#39;");
	}
	
	void setFirstName(String fn) {
		this.firstName = (fn==null?"":fn);
	}
	
	void setLastName(String ln) {
		this.lastName = (ln==null?"":ln);
	}
	
	String getFullName() {
		try {
			return (this.firstName.isEmpty()?"":this.firstName) + (this.lastName.isEmpty()?"":" " + this.lastName);
		} catch (Exception e) {
			if (this.firstName==null) this.firstName = "";
			if (this.lastName==null) this.lastName = "";
			return this.getFullName();
		}
	}

	String getPublicName() {
		if (this.anonymous) return "Anonymous";
		else return this.getFullName();
	}
	
	static String getPublicName(String userId) {
		User user = ofy.get(User.class,userId);
		if (user.isAnonymous()) return "Anonymous";
		else return user.getFullName();
	}
	
	String getBothNames() {
		return lastName + " " + lastName;
	}
	
	boolean isAnonymous() {
		return this.anonymous;
	}
	
	void setAnonymous(boolean makeAnonymous) {
		this.anonymous = makeAnonymous;
	}
	
	int getRoles() {
		return this.roles;
	}
	
	void setRoles(int roles) {
		this.roles = roles;
		// key to roles:
		// -1 = deactivated (no site privileges)
		//  0 = new account
		//  1 = student
		//  2 = contributor
		//  4 = editor
		//  8 = teaching assistant
		// 16 = instructor
		// 32 = administrator
		// any valid combination, e.g. 19 = 16 + 2 + 1 (Instructor/Contributor/Student)
	}
	
	public String getPrincipalRole() {
		String principalRole;
		if (roles < 0) principalRole = "Deactivated";
		else if (roles == 0) principalRole = "New Account";
		else if (roles == 1) principalRole = "Student";
		else if (roles < 4) principalRole = "Contributor";
		else if (roles < 8) principalRole = "Editor";
		else if (roles < 16) principalRole = "Teaching Assistant";
		else if (roles < 32) principalRole = "Instructor";
		else if (roles < 64) principalRole = "Administrator";
		else principalRole = ""; // unknown role
		return principalRole;
	}
	
	boolean isAdministrator() {
		return ((roles%64)/32 == 1);
	}

	void setIsAdministrator(boolean makeAdmin) {
		if (isAdministrator() && !makeAdmin) roles -= 32;
		else if (!isAdministrator() && makeAdmin) roles += 32;
	}
	
	boolean isInstructor() {
		return ((roles%32)/16 == 1);
	}

	void setIsInstructor(boolean makeInstructor) {
		if (isInstructor() && !makeInstructor) roles -= 16;
		else if (!isInstructor() && makeInstructor) roles += 16;
	}
	
	boolean isTeachingAssistant() {    
		return ((roles%16)/8 == 1);
	}
	
	void setIsTeachingAssistant(boolean makeTA) {
		if (isTeachingAssistant() && !makeTA) roles -= 8;
		else if (!isTeachingAssistant() && makeTA) roles += 8;
	}
	
	boolean isEditor() {
		return ((roles%8)/4 == 1);
	}

	void setIsEditor(boolean makeEditor) {
		if (isEditor() && !makeEditor) roles -= 4;
		else if (!isEditor() && makeEditor) roles += 4;
	}
	
	boolean isContributor() {
		return ((roles%4)/2 == 1);
	}

	void setIsContributor(boolean makeContributor) {
		if (isContributor() && !makeContributor) roles -= 2;
		else if (!isContributor() && makeContributor) roles += 2;
	}
	
	boolean isStudent() {
		return roles>0; // everyone has student privileges except deactivated accounts
	}
	
	boolean isDeactivated() {
		return roles<0;  // roles = -1 for deactivated accounts
	}
	
	void activate() {
		roles = 1;  // useful for reactivating a deactivated account
	}
	
	void deactivate() {
		roles = -1;  // removes all user privileges
	}
	
	String getAuthDomain() {
		return this.authDomain;
	}
	
	void setAuthDomain(String ad) {
		this.authDomain = ad;
	}
	
	Date getLastLogin() {
		if (this.lastLogin != null) return this.lastLogin;
		else return new Date(0);
	}
	
	void setLastLogin() {
		this.lastLogin = new Date();
	}
	
	Date getTermsAgreedTo() {
		return this.termsAgreedTo;
	}
	
	 void setTermsAgreedTo() {
		 this.termsAgreedTo = new Date();
	 }
	 
	 boolean needsNewTermsAgreement() {
		 try {
			 return termsAgreedTo==null?true:termsAgreedTo.before(About.termsUpdated);
		 } catch (Exception e) {
			 return false;
		 }
	 }
	 
	public int compareTo(User other) {
		int compare = this.lastName.toLowerCase().compareTo(other.lastName.toLowerCase());
		if (compare == 0) compare = this.firstName.toLowerCase().compareTo(other.firstName.toLowerCase());
		if (compare == 0) compare = this.email.compareTo(other.email);
		return compare;
	}
}
